﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using BitcoinCommon;
using BitcoinExchange;
using BitcoinExchange.Data;
using BitcoinMarket;
using BitcoinTrading.Data;

namespace BitcoinTrading.Trading
{
    public class PassiveTrader
    {
        double MarginalAsk = 2000.0;
        double MarginalBid = -1000.0;
        double HedgeVolume = 2.0;

        double BTCeDiscountRate = 1.0;
        double MarginRate = 0.30;
        double BTCeFeeRate = 0.2;

        DataPassiveOrder AskOrder;
        DataPassiveOrder BidOrder;

        MainInfo _info;
        DataController _data;
        Exchange _exchange;
        MarketPrice _market;

        public PassiveTrader(MainInfo info, Exchange exchange, DataController data, MarketPrice market)
        {
            _info = info;
            _exchange = exchange;
            _data = data;
            _market = market;
            AskOrder = new DataPassiveOrder(ePassiveOrder.ASK);
            BidOrder = new DataPassiveOrder(ePassiveOrder.BID);
        }

        public double GetMarginalBuy()
        {
            return MarginalAsk;
        }

        public double GetMarginalSell()
        {
            return MarginalBid;
        }

        public void SetMarginalPrice(double dbMarginalBuy, double dbMarginalSell)
        {
            MarginalAsk = dbMarginalBuy;
            MarginalBid = dbMarginalSell;
        }

        public void InitializedTrade()
        {
            ClearOrder();
        }

        public void ExecuteTrade()
        {
            ExecuteTradeAsk();

            ExecuteTradeBid();

            CheckUnableOrder();
        }

        private void CheckUnableOrder()
        {
            Dictionary<string, string> dicInfo;

            _exchange.RetrievedOpenOrder();

            foreach (string strOrderID in _exchange.KorbitOpenOrderList())
            {
                dicInfo = _exchange.KorbitOrderInfo(strOrderID);

                if (dicInfo[DataOpenOrder.COLUMN_ORDER_ID] == "")
                    continue;

                if (dicInfo[DataOpenOrder.COLUMN_ORDER_TYPE].ToUpper() == "SELL")
                {
                    if (AskOrder.OrderID != dicInfo[DataOpenOrder.COLUMN_ORDER_ID])
                    {
                        _exchange.KorbitOrderCancel(dicInfo[DataOpenOrder.COLUMN_ORDER_ID]);
                    }
                }
                else if (dicInfo[DataOpenOrder.COLUMN_ORDER_TYPE].ToUpper() == "BUY")
                {
                    if (BidOrder.OrderID != dicInfo[DataOpenOrder.COLUMN_ORDER_ID])
                    {
                        _exchange.KorbitOrderCancel(dicInfo[DataOpenOrder.COLUMN_ORDER_ID]);
                    }
                }
            }
            
        }

        private void ExecuteTradeAsk()
        {
            string strOrderID;
            double dbAskPrice;
            double dbEstAskPrice;
            double dbBTCAmount;
            Dictionary<string, string> dicOrderInfo;

            strOrderID = AskOrder.OrderID;
            dbEstAskPrice = EstKorbitAsk();

            dicOrderInfo = _exchange.KorbitOrderInfo(strOrderID);
                        
            if (strOrderID == "") //최초 Order일 경우 그냥 새 주문을 넣고 리턴
            {
                SendOrderAsk();
                return;
            }
            else if (dicOrderInfo[DataOpenOrder.COLUMN_ORDER_ID] == "") //전부 매매 되었을때 헷지를 하고 새로 주문을 넣는다.
            {
                HedgeAsk(HedgeVolume);
                SendOrderAsk();
                return;
            }

            //주문 가격을 넣는다.
            dbAskPrice = Double.Parse(dicOrderInfo[DataOpenOrder.COLUMN_BTC_PRICE]);
            dbBTCAmount = Math.Floor(Double.Parse(dicOrderInfo[DataOpenOrder.COLUMN_BTC_OPEN_VOLUME]) / 0.1 ) * 0.1;

            //가격이 변동 되었을때
            if (dbAskPrice != dbEstAskPrice)
            {
                ChangePriceAsk(strOrderID, dbEstAskPrice);
            }
            else if (dbBTCAmount < (HedgeVolume / 2)) //남은 물량이 0.5 이하이면 바로 없앤다.
            {
                HedgeAsk(HedgeVolume - dbBTCAmount);
                SendOrderAsk();
                return;
            }
            else
            {
                AskOrder.OpenAmount = dbBTCAmount;
            }

        }

        private void ExecuteTradeBid()
        {

            string strOrderID;
            double dbBidPrice;
            double dbEstBidPrice;
            double dbBTCAmount;
            Dictionary<string, string> dicOrderInfo;

            strOrderID = BidOrder.OrderID;
            dbEstBidPrice = EstKorbitBid();

            dicOrderInfo = _exchange.KorbitOrderInfo(strOrderID);

            if (strOrderID == "") //최초 Order일 경우 그냥 새 주문을 넣고 리턴
            {
                SendOrderBid();
                return;
            }
            else if (dicOrderInfo[DataOpenOrder.COLUMN_ORDER_ID] == "") //전부 매매 되었을때 헷지를 하고 새로 주문을 넣는다.
            {
                HedgeBid(HedgeVolume);
                SendOrderBid();
                return;
            }

            //주문 가격을 넣는다.
            dbBidPrice = Double.Parse(dicOrderInfo[DataOpenOrder.COLUMN_BTC_PRICE]);
            dbBTCAmount = Math.Floor(Double.Parse(dicOrderInfo[DataOpenOrder.COLUMN_BTC_OPEN_VOLUME]) / 0.1) * 0.1;

            //가격이 변동 되었을때
            if (dbBidPrice != dbEstBidPrice)
            {
                ChangePriceBid(strOrderID, dbEstBidPrice);
            }
            else if (dbBTCAmount < (HedgeVolume / 2.0)) //남은 물량이 0.5 이하이면 바로 없앤다.
            {
                HedgeBid(HedgeVolume - dbBTCAmount);
                SendOrderBid();
                return;
            }
            else
            {
                BidOrder.OpenAmount = dbBTCAmount;
            }

        }

        private bool CanAskOrder()
        {
            bool bReturn = false;

            //BTCe 매수 할 수 없는 경우
            if (_exchange.USDAmount() > Double.Parse(_data.BTCeBestAsk()) * this.HedgeVolume *  1.1)
            {
                bReturn = true;
            }

            return bReturn;
        }

        private bool CanBidOrder()
        {
            bool bReturn = false;

            if (_exchange.BTCeBTCAmount() > this.HedgeVolume * 1.1)
            {
                bReturn = true;
            }

            //BTCe 매도 할 수 없는 경우

            return bReturn;
        }

        private double EstKorbitAsk()
        {
            double dbBitstampAskPrice;
            double dbBTCeAskPrice;
            double dbKorbitAskPrice;
            double dbCurrency;

            dbCurrency = Double.Parse(_market.CurrentRate());
            dbBitstampAskPrice = _market.BitStampAskPrice();
            dbBTCeAskPrice = Double.Parse(_data.BTCeBestAsk());

            double dbBitstampToBTCeRate;

            //디스카운트 비율
            dbBitstampToBTCeRate = dbBTCeAskPrice / dbBitstampAskPrice * 100;
            dbBitstampToBTCeRate = dbBitstampToBTCeRate + BTCeDiscountRate;
            dbKorbitAskPrice = dbBitstampAskPrice * (100.0 + MarginRate + BTCeFeeRate + 0.5) / 100.0 * dbCurrency ;

            if (dbBitstampToBTCeRate > 100.0)
            {
                dbKorbitAskPrice = dbKorbitAskPrice * dbBitstampToBTCeRate / 100.0;
            }


            dbKorbitAskPrice = Math.Ceiling(dbKorbitAskPrice / 200.0) * 200.0;

            return dbKorbitAskPrice;
        }

        private double EstKorbitBid()
        {
            double dbBitstampBidPrice;
            double dbBTCeBidPrice;
            double dbKorbitBidPrice;
            double dbCurrency;

            dbCurrency = Double.Parse(_market.CurrentRate());
            dbBitstampBidPrice = _market.BitStampBidPrice();
            dbBTCeBidPrice = Double.Parse(_data.BTCeBestBid());

            double dbBitstampToBTCeRate;
            dbBitstampToBTCeRate = dbBTCeBidPrice / dbBitstampBidPrice * 100;
            dbBitstampToBTCeRate = dbBitstampToBTCeRate + BTCeDiscountRate;
            dbKorbitBidPrice = dbBitstampBidPrice * (100.0 - MarginRate - BTCeFeeRate + 0.1) / 100.0 * dbCurrency;

            if (dbBitstampToBTCeRate < 100.0)
            {
                dbKorbitBidPrice = dbKorbitBidPrice * dbBitstampToBTCeRate / 100.0;
            }


            dbKorbitBidPrice = Math.Floor(dbKorbitBidPrice / 200.0) * 200.0;

            return dbKorbitBidPrice;
        }

        private void SendOrderAsk()
        {
            string strAskOrderID;
            double dbKorbitAskPrice;

            dbKorbitAskPrice = EstKorbitAsk();

            AskOrder.BTCPrice = dbKorbitAskPrice;
            AskOrder.BTCAmount = HedgeVolume;
            AskOrder.OpenAmount = HedgeVolume;
            AskOrder.TotalContractPrice = 0.0;


            if (CanAskOrder()) //매매할 여력이 있는 경우
            {
                strAskOrderID = _exchange.KorbitOrderSell(AskOrder.BTCPrice.ToString(), AskOrder.OpenAmount.ToString());
                AskOrder.OrderID = strAskOrderID;
            }
            else //매매할 여력이 없는 경우
            {
                AskOrder.OrderID = "";
            }

        }

        private void SendOrderBid()
        {
            string strBidOrderID;
            double dbKorbitBidPrice;
            
            dbKorbitBidPrice = EstKorbitBid();

            BidOrder.BTCPrice = dbKorbitBidPrice;
            BidOrder.BTCAmount = HedgeVolume;
            BidOrder.OpenAmount = HedgeVolume;
            BidOrder.TotalContractPrice = 0.0;

            if (CanBidOrder())
            {
                strBidOrderID = _exchange.KorbitOrderBuy(BidOrder.BTCPrice.ToString(), BidOrder.OpenAmount.ToString());
                BidOrder.OrderID = strBidOrderID;
            }
            else
            {
                BidOrder.OrderID = "";
            }

        }

        private void SendOrder()
        {
            SendOrderAsk();

            SendOrderBid();
        }

        private void SendHedge()
        {
            
        }

        public void ClearOrder()
        {
            foreach (string strOrderID in _exchange.KorbitOpenOrderList())
            {
                _exchange.KorbitOrderCancel(strOrderID);
            } 
        }

        public void ClearHedgeOrder()
        {
            //기존 매매를 모두 지운다.
            double dbContractVolume;
            string strOrderType;
            Dictionary<string, string> dicOrderInfo;

            foreach(string strOrderID in _exchange.KorbitOpenOrderList())
            {
                //남은 체결 분량을 헷지한다.
                dicOrderInfo = _exchange.KorbitOrderInfo(strOrderID);

                strOrderType = dicOrderInfo[DataOpenOrder.COLUMN_ORDER_TYPE];
                dbContractVolume = HedgeVolume - Double.Parse(dicOrderInfo[DataOpenOrder.COLUMN_BTC_OPEN_VOLUME].ToString());

                if (strOrderType.ToUpper() == "BUY")
                {
                    HedgeBid(dbContractVolume);
                }
                else
                {
                    HedgeAsk(dbContractVolume);
                }

                //기존 매매를 지운다.
                _exchange.KorbitOrderCancel(strOrderID);
            }            
        }

        private void ChangePriceAsk(string strOrderID, double dbBTCPrice)
        {
            string strNewOrderID;
            Dictionary<string, string> dicInfo;
            double dbAmount;

            dicInfo = _exchange.KorbitOrderInfo(strOrderID);
            dbAmount = Math.Floor(Double.Parse(dicInfo[DataOpenOrder.COLUMN_BTC_OPEN_VOLUME]) / 0.05) * 0.05;

            _exchange.KorbitOrderCancel(strOrderID);

            strNewOrderID = _exchange.KorbitOrderSell(dbBTCPrice.ToString(), dbAmount.ToString());

            AskOrder.OrderID = strNewOrderID;
            AskOrder.BTCPrice = dbBTCPrice;
            AskOrder.OpenAmount = dbAmount;

        }

        private void ChangePriceBid(string strOrderID, double dbBTCPrice)
        {
            string strNewOrderID;
            Dictionary<string, string> dicInfo;
            double dbAmount;

            dicInfo = _exchange.KorbitOrderInfo(strOrderID);
            dbAmount = Math.Floor(Double.Parse(dicInfo[DataOpenOrder.COLUMN_BTC_OPEN_VOLUME]) / 0.05) * 0.05;

            _exchange.KorbitOrderCancel(strOrderID);

            strNewOrderID = _exchange.KorbitOrderBuy(dbBTCPrice.ToString(), dbAmount.ToString());

            BidOrder.OrderID = strNewOrderID;
            BidOrder.BTCPrice = dbBTCPrice;
            BidOrder.OpenAmount = dbAmount;
        }

        private void HedgeAsk(double dbVolume)
        {
            double dbBTCPrice;

            dbBTCPrice = Double.Parse(_data.BTCeBestAsk());

            _exchange.BTCeOrderBuy(dbBTCPrice.ToString(), dbVolume.ToString());
        }

        private void HedgeBid(double dbVolume)
        {
            double dbBTCPrice;

            dbBTCPrice = Double.Parse(_data.BTCeBestBid());

            _exchange.BTCeOrderSell(dbBTCPrice.ToString(), dbVolume.ToString());
        }
    }
}
